package com.citycloud.ccuap.tc.oauth2.config;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.servlet.http.HttpServletRequest;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.session.data.redis.RedisIndexedSessionRepository;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.citycloud.ccuap.commons.application.Application;
import com.citycloud.ccuap.commons.json.JSONData;
import com.citycloud.ccuap.commons.web.util.ResolveRequest;
import com.citycloud.ccuap.tc.common.constant.OAuth2Constant;
import com.citycloud.ccuap.tc.oauth2.feignclient.SysFCService;

/**
 * 1、主要处理 TokenEndpoint 中 /oauth/token 接口生成 OAuth2AccessToken 后与当前请求的 session
 * 关联起来，以便请求进入网关后可以从redis中获取 OAuth2 access_token 对应的 session id</br>
 * 2、可以自定义token携带内容
 */
public class CustomTokenEnhancer implements TokenEnhancer {
	@Override
	public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
		Map<String, Object> additionalInfo = new LinkedHashMap<>(accessToken.getAdditionalInformation());
		// eg：自定义token内容，加入username
		additionalInfo.put("username", authentication.getName());
		((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);

		org.springframework.security.core.userdetails.User user = (User) authentication.getPrincipal();
		// 执行关联操作
		this.associateRequest(accessToken.getValue(), user.getUsername());

		return accessToken;
	}

	/**
	 * 关联OAuth2 access_token 和 session id
	 *
	 * @param accessToken /oauth/token 接口要返回的 access_token
	 * @param userNo      登录的用户编号
	 */
	private void associateRequest(String accessToken, String userNo) {
		RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
		HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();

		String logonIp = ResolveRequest.getIpAddress(request);

		// 通过feign-client方式将用户相关信息写入redis session，并返回session id
		SysFCService fcService = Application.getClassBean(SysFCService.class);
		JSONData<String> oauth2UserInfo = fcService.updateOauth2UserSession(userNo, logonIp);
		String sessionId = oauth2UserInfo.getData();

		// 获取session有效期
		StringRedisTemplate redisTemplate = Application.getClassBean(StringRedisTemplate.class);
		String sessionKey = RedisIndexedSessionRepository.DEFAULT_NAMESPACE + ":sessions:" + sessionId;
		Long timeout = redisTemplate.getExpire(sessionKey);
		// 关联OAuth2 access_token 和用户的session id
		redisTemplate.opsForValue().set(OAuth2Constant.DEFAULT_KEY_NAMESPACE + accessToken, sessionId, timeout,
				TimeUnit.SECONDS);
	}
}